Software development continues to evolve through stages of increasing sophistication to accommodate the needs of developers, users, and system configurations. Early programs were written directly into machine code (i.e., program code that a hardware platform could execute). More recently, a programmer can generate source code in one or more source code languages, compile the source code into object code, and link the object code into executable or machine code. Such evolution of software technologies continues into new areas, particularly with the availability of dynamic runtime environments.
In a shift away from the traditional programming approaches, dynamic runtime environments have presented improved opportunities for the implementation and inter-operation of multiple source languages by blurring the division between compile-time and run-time. For example, a dynamic runtime environment may execute programs originating from multiple source code languages by compiling the original source code into a platform-independent intermediate language. The intermediate language code can then be executed within the dynamic runtime environment on a given hardware platform, with or without a compilation of the intermediate language code into executable code. Compiled code typically achieves faster execution speeds.
One objective of dynamic runtime environments is to support as much functionality of the original source code languages as possible. Ideally, a dynamic execution environment and the associated intermediate language would support any possible feature (e.g., an operation, type, or control flow construct) of any source code language. However, complete global language support has not yet been attained.
One problem with existing dynamic runtime environments is the lack of efficient support for parametric polymorphism, in which a routine or class is parameterized by one or more type parameters. In a contrast to subtype polymorphism, which is useful for defining generic behavior over a set of related types, parametric polymorphism is useful for defining generic behavior where the type parameters need not be related within the type hierarchy. An example of parametric polymorphism in a source code language is demonstrated by templates in the C++ programming language.
In one existing approach to parametric polymorphism, a compiler/linker detects a polymorphic method or class in the source code and generates distinct instantiations of the polymorphic method or class for each type parameter at compilation/link time (a process known as “specialization”). For example, if “array<T>” is a parameterized type, it may be compiled and linked as both an “array<int>”, an array of integers, and as an “array<char>”, an array of characters. Regardless of whether both an “array<int>” and an “array<char>” are actually operated on during execution, the specialized code is generated to support an array type for each type of element defined in the program. Drawbacks to this approach include code explosion, a lack of support for truly separate compilation, and the lack of support for dynamic linking.
Dynamic execution environments have certain characteristics that have presented obstacles to the efficient support of parameterized polymorphism, including lazy loading and analysis of code for polymorphic classes and methods (i.e., generic code), sharing of code among one or more typing contexts, polymorphic recursion (wherein type parameters may be nested so that parameterized classes may use themselves recursively as larger types than those provided as arguments) and dynamic generation of replacement generic code for existing methods or new methods with generic classes. Furthermore, existing approaches for implementing parametric polymorphism have limitations (e.g., static pre-allocation of compilation data structures) that preclude efficient solutions in a dynamic execution environment.